package cool.houge.pig.infra.service.token;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import cool.houge.pig.AuthGrantType;
import cool.houge.pig.model.AuthClient;
import cool.houge.pig.repository.auth.TokenRepository;
import cool.houge.pig.repository.user.UserRepository;
import cool.houge.pig.service.auth.AuthTokenException;
import cool.houge.pig.service.auth.AuthTokenException.Error;
import cool.houge.pig.service.auth.AuthTokenInput;
import cool.houge.pig.service.auth.TokenPreprocess;
import java.util.Objects;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

/**
 * 刷新令牌.
 *
 * @author KK (kzou227@qq.com)
 */
@Component
public class RefreshTokenTokenPreprocess implements TokenPreprocess {

  private final TokenRepository tokenRepository;
  private final UserRepository userRepository;
  private final Algorithm algorithm;

  /**
   * @param tokenRepository
   * @param userRepository
   * @param algorithm
   */
  public RefreshTokenTokenPreprocess(
      TokenRepository tokenRepository, UserRepository userRepository, Algorithm algorithm) {
    this.tokenRepository = tokenRepository;
    this.userRepository = userRepository;
    this.algorithm = algorithm;
  }

  @Override
  public String grantType() {
    return AuthGrantType.REFRESH_TOKEN.getGrantType();
  }

  @Override
  public Mono<ProcessPart> process(AuthClient authClient, AuthTokenInput input) {
    DecodedJWT jwt;
    try {
      jwt = JWT.require(algorithm).build().verify(input.getRefreshToken());
    } catch (JWTDecodeException e) {
      throw new AuthTokenException(Error.INVALID_REQUEST, "非法的刷新令牌");
    } catch (JWTVerificationException e) {
      throw new AuthTokenException(Error.INVALID_REQUEST, "刷新令牌验证失败");
    }

    long id = Long.parseLong(jwt.getId());
    return tokenRepository
        .findById(id)
        .doOnNext(
            token -> {
              // 刷新令牌不相同
              if (!Objects.equals(input.getRefreshToken(), token.getRefreshToken())) {
                throw new AuthTokenException(Error.INVALID_REQUEST, "刷新令牌不匹配");
              }
              // 获取刷新令牌与使用刷新令牌的客户端不相同
              if (!Objects.equals(input.getClientId(), token.getClientId())) {
                throw new AuthTokenException(Error.INVALID_CLIENT, "客户端ID与刷新令牌不匹配");
              }
            })
        .flatMap(
            token -> {
              //
              return userRepository
                  .findById(token.getUid())
                  .map(user -> ProcessPart.builder().user(user).build());
            });
  }
}
